package com.gopay.common.cipher.algorithm;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gopay.common.cipher.algorithm.baseAlgorithm.Cipher3DES;
import com.gopay.common.cipher.algorithm.baseAlgorithm.Decrypt;
import com.gopay.common.cipher.algorithm.impl.CipherAlgorithmWithCode;
import com.gopay.common.cipher.constants.EncryptType;
import com.gopay.common.cipher.constants.SensitiveInfoConstants;
import com.gopay.common.cipher.vo.SecurityKeyInfo;



/**
 * 
 * @ClassName: CipherAlgorithmContext 
 * @Description: 根据密文及keyInfo选择不同的策略，对密文实现加解密 
 * @author zyt 
 * @date 2016年11月30日 下午4:27:02
 */
public class CipherAlgorithmContext {

	private static Logger logger = LoggerFactory.getLogger(CipherAlgorithmContext.class);
	
	/**
	 * 
	 * @Description: 	根据keyInfo中的信息，选择使用哪个实现 CipherAlgorithm接口的加解密类
	 * @param keyInfo	data key信息
	 * @return 			选择的加解密类
	 * @author princezhaoyt
	 */
	private static CipherAlgorithm getCipherAlgorithm(SecurityKeyInfo keyInfo){
		//目前只返回SensitiveInfoEncrypt类，后续接入机密级后，需要指定策略
		if(keyInfo != null){
			return new CipherAlgorithmWithCode();
		} else {
			return null;
		}
	}
	
	/**
	 * 
	 * @Description: 			根据keyInfo解密 
	 * @param cipherContent		密文
	 * @param keyInfo			data key的信息，若为空，代表未在数据库中存储密钥（目前有DES和3DES2KEY），只根据密文前缀判断使用之前的哪个密钥哪种方式做加解密。
	 * @return 					解密成功，返回明文，失败，返回原文
	 * @author princezhaoyt
	 */
	public static String decryptByKeyInfo(String cipherContent, SecurityKeyInfo keyInfo) throws Exception {
		if (keyInfo == null) {
			EncryptType encrypt = getEncryptType(cipherContent);
			if (encrypt == null || StringUtils.isBlank(cipherContent)) {
				return cipherContent;
			}
			String resContent = cipherContent;
			if (encrypt == EncryptType.DES) {
				// des加密的前缀
				Decrypt dec;
				dec = new Decrypt();
				String ciphertext_b = resContent.replace(SensitiveInfoConstants.ENCRYPT_MARK_DES, "");
				resContent = dec.decrypt(ciphertext_b);
			} else if (encrypt == EncryptType.THREE_DES_TWO_KEYS) {
				// 3des 2key加密的前缀
				resContent = resContent.replace(SensitiveInfoConstants.ENCRYPT_MARK_3DES_2KEY, "");
				byte[] resNoByte;
				resNoByte = Cipher3DES.decryptBase64(SensitiveInfoConstants.key3des2keyBytes,
						resContent.getBytes());
				resContent = new String(resNoByte);
			}

			return resContent;
		} else {
			CipherAlgorithm cipher = getCipherAlgorithm(keyInfo);
			//去除前缀，得到真正密文
			String realCiphertext = cipherContent.replace(keyInfo.getPrefix(), "");
			String realText = cipher.decrypt(realCiphertext, keyInfo);
			//modified by zyt 2017-7-10，解密失败的时候，返回之前的密文，解决部分解密失败的时候，返回不带前缀密文的bug
			if(StringUtils.equals(realCiphertext, realText)){
				logger.error("解密失败，返回原文：{}", cipherContent);
				return cipherContent;
			}
			return realText;
		}
	}
	
	/**
	 * 
	 * @Description: 		根据keyInfo加密 
	 * @param content		明文 
	 * @param keyInfo		datakey的信息
	 * @return 				密文
	 * @author princezhaoyt
	 */
	public static String encryptByKeyInfo(String content, SecurityKeyInfo keyInfo) throws Exception{
		CipherAlgorithm cipher = getCipherAlgorithm(keyInfo);
		if(cipher == null ){
			logger.error("加密算法获取失败，加密失败");
			return content;
		}
		
		String cipherContent = cipher.encrypt(content, keyInfo);
		if(StringUtils.isNotBlank(cipherContent) && !StringUtils.equals(content, cipherContent)){
			//返回密文时，添加前缀标志
			return keyInfo.getPrefix() + cipherContent;
		}
		return cipherContent;
	}
	
	/**
	 * 
	 * @Description: 		传入密文，根据密文前缀判断使用的何种加密方式 
	 * @param cipherText	密文
	 * @return 				加密方式
	 * @author 				zyt
	 */
	public static EncryptType getEncryptType(String cipherText) {
		
		if (StringUtils.isNotEmpty(cipherText)) {
			if (cipherText.startsWith(SensitiveInfoConstants.ENCRYPT_MARK_3DES_2KEY)) {
				//3des 2key加密的前缀
				return EncryptType.THREE_DES_TWO_KEYS;
			} else if (cipherText.startsWith(SensitiveInfoConstants.ENCRYPT_MARK_3DES_3KEY)) {
				//3des 3key加密的前缀
				return EncryptType.THREE_DES_THREE_KEYS;
			} else if (cipherText.startsWith(SensitiveInfoConstants.ENCRYPT_MARK_DES)) {
				//des加密的前缀
				return EncryptType.DES;
			} else if(StringUtils.contains(cipherText, SensitiveInfoConstants.ENCRYPT_COMMON_MARK)){
				logger.error("未知的加密方式，无法解密");
			}
		}

		return null;
	}
	
}
